// --*- C++ -*------x---------------------------------------------------------
// $Id: Bond.cc,v 1.4 2008-05-09 13:58:33 biocomp Exp $
//
// Class:           Bond
// 
// Base class:      SimpleBond
//
// Derived classes: - 
//
// Author:          Silvio Tosatto
//
// Project name:    -
//
// Date:            08/99-11/99
//
// Description:     Defines chemical and abstract bonds between objects 
//                  which are compositions of atoms. Eg. 'bond' between 
//                  two amino acids.
//                  Attention: copy() strips orig from its bonds and 
//                  attaches them to the new bond. 
// 
// -----------------x-------------------x-------------------x-----------------

#include <algorithm>
#include <Bond.h>

using namespace Biopool;

Bond::Bond(unsigned int mI, unsigned int mO) : SimpleBond(mI, mO), 
  inRef(), outRef()
{ 
  PRINT_NAME; 
  inRef.reserve(mI);
  outRef.reserve(mO);
}

Bond::Bond(const Bond& orig) 
{ PRINT_NAME; copy(orig); }

Bond::~Bond()
{
  PRINT_NAME;
  while (!inRef.empty())
    inRef.pop_back();
  while (!outRef.empty())
    outRef.pop_back();
}


// PREDICATES

// -*- C++ -*-----------------------------------------------------------------
//
//  Method:        Bond::getOpenInBond
//
//  Author:        Silvio Tosatto 
//
//  Date:          02/00
//
//  Description:
//    Returns reference to the i-th open in-bond atom.
//    NB: This is undefined (ie. returns error) for Bond, but is needed for
//    later structures (AminoAcid, etc.).
//
// ----------------------------------------------------------------------------
Atom&
Bond::getOpenInBondRef(unsigned int n) 
{
  ERROR("getOpenInBondRef() undefined for this class.", exception);
  return *inRef[n];
}

const Atom&
Bond::getOpenInBondRef(unsigned int n) const
{
  ERROR("getOpenInBondRef() undefined for this class.", exception);
  return *inRef[n];
}


// -*- C++ -*-----------------------------------------------------------------
//
//  Method:        Bond::getOpenOutBond
//
//  Author:        Silvio Tosatto 
//
//  Date:          02/00
//
//  Description:
//    Returns reference to the i-th open out-bond atom.
//    NB: This is undefined (ie. returns error) for Bond, but is needed for
//    later structures (AminoAcid, etc.).
//
// ----------------------------------------------------------------------------
Atom&
Bond::getOpenOutBondRef(unsigned int n) 
{
  ERROR("getOpenOutBondRef() undefined for this class.", exception);
  return *outRef[n];
}

const Atom&
Bond::getOpenOutBondRef(unsigned int n) const
{
  ERROR("getOpenOutBondRef() undefined for this class.", exception);
  return *outRef[n];
}

// MODIFIERS

// -*- C++ -*-----------------------------------------------------------------
//
//  Method:        Bond::bindIn
//
//  Author:        Silvio Tosatto 
//
//  Date:          08/99
//
//  Description:
//    In-going bond from this to c, from this' atom _this to c's _other atom. 
//
// ----------------------------------------------------------------------------
void
Bond::bindIn(Atom& _this, Bond& c, Atom& _other)
{
  if (isInBond(c))  // already bound
    {
      _this.bindIn(_other); // check if atoms are already bound as well
      return;
    };

  PRECOND((sizeInBonds() < maxIn), exception);
  PRECOND((c.sizeOutBonds() < c.maxOut), exception);

  inBonds.push_back(&c);
  c.outBonds.push_back(this);
  inRef.push_back(&_this);
  c.outRef.push_back(&_other);

  _this.bindIn(_other); // bind the atoms
}


// -*- C++ -*-----------------------------------------------------------------
//
//  Method:        Bond::bindOut
//
//  Author:        Silvio Tosatto 
//
//  Date:          08/99
//
//  Description:
//    Out-going bond from this to c, from this' atom _this to c's _other atom. 
//
// ----------------------------------------------------------------------------
void
Bond::bindOut(Atom& _this, Bond& c, Atom& _other)
{
  if (isOutBond(c))  // already bound
    {
      _this.bindOut(_other); // check if atoms are already bound as well
      return;
    };

  PRECOND((sizeOutBonds() < maxOut), exception);
  PRECOND((c.sizeInBonds() < c.maxIn), exception);

  outBonds.push_back(&c);
  c.inBonds.push_back(this);
  outRef.push_back(&_this);
  c.inRef.push_back(&_other);

  _this.bindOut(_other); // bind the atoms
}


// -*- C++ -*-----------------------------------------------------------------
//
//  Method:        Bond::unbindIn
//
//  Author:        Silvio Tosatto 
//
//  Date:          08/99
//
//  Description:
//    Remove in-going bond from this to c. 
//
// ----------------------------------------------------------------------------
void
Bond::unbindIn(Bond& c)
{
  PRINT_NAME;
  pUnbindIn(c, true);
  c.pUnbindOut(*this);
}


// -*- C++ -*-----------------------------------------------------------------
//
//  Method:        Bond::unbindOut
//
//  Author:        Silvio Tosatto 
//
//  Date:          08/99
//
//  Description:
//    Remove out-going bond from this to c. 
//
// ----------------------------------------------------------------------------
void
Bond::unbindOut(Bond& c)
{
  PRINT_NAME;
  pUnbindOut(c, true);
  c.pUnbindIn(*this);
}


// -*- C++ -*-----------------------------------------------------------------
//
//  Method:        Bond::pUnbindIn
//
//  Author:        Silvio Tosatto 
//
//  Date:          08/99
//
//  Description:
//    Private helper method for removing in-bonds. 
//
// ----------------------------------------------------------------------------
void
Bond::pUnbindIn(Bond& c, bool unbind)
{
  PRINT_NAME;
  int index = 0;

  for (unsigned int i = 0; i < inBonds.size(); i++)
    if (*inBonds[i] == dynamic_cast<SimpleBond&>(c))
      {
	index = i;
	inBonds.erase(inBonds.begin()+i);
	break;
      }
  
  if (index == 0)
    DEBUG_MSG("Bond::pUnbindIn(Bond& c): WARNING: Bond not found.");
  else
    {
      if (unbind)
	{
	  // find bound atom in c
	  int index2 = 0;
	  for (unsigned int i = 0; i < c.outRef.size(); i++)
	    if (c.outRef[i] == inRef[index])
	    {
	      index2 = i;
	      break;
	    }
	  inRef[index]->unbindIn(*c.outRef[index2]);
	}
      inRef.erase(inRef.begin()+index); // delete refIn  
    }
}


// -*- C++ -*-----------------------------------------------------------------
//
//  Method:        Bond::pUnbindOut
//
//  Author:        Silvio Tosatto 
//
//  Date:          08/99
//
//  Description:
//    Private helper method for removing out-bonds. 
//
// ----------------------------------------------------------------------------
void
Bond::pUnbindOut(Bond& c, bool unbind)
{
  PRINT_NAME;
  int index = 0;

  for (unsigned int i = 0; i < outBonds.size(); i++)
    if (*outBonds[i] == dynamic_cast<SimpleBond&>(c))
      {
	index = i;
	outBonds.erase(outBonds.begin()+i);
	break;
      }
  
  if (index == 0)
    DEBUG_MSG("Bond::pUnbindOut(Bond& c): WARNING: Bond not found.");
  else
    {
      if (unbind)
	{
	  // find bound atom in c
	  int index2 = 0;
	  for (unsigned int i = 0; i < c.inRef.size(); i++)
	    if (c.inRef[i] == outRef[index])
	    {
	      index2 = i;
	      break;
	    }
	  outRef[index]->unbindOut(*c.inRef[index2]);
	}
      outRef.erase(outRef.begin()+index); // delete refOut  
    }
}


// -*- C++ -*-----------------------------------------------------------------
//
//  Method:        Bond::copy
//
//  Author:        Silvio Tosatto 
//
//  Date:          08/99
//
//  Description:
//    Copy operator. Attention: copy() strips orig from its bonds and 
//    attaches them to the new bond. 
//
// ----------------------------------------------------------------------------
void 
Bond::copy(const Bond& orig)
{
  PRINT_NAME;
  SimpleBond::copy(orig);

  inRef = orig.inRef;
  outRef = orig.outRef;
}

